<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="/Public/js/three.min.js"></script>
    <script src="/Public/js/three_shaders.js"></script>
    <script src="/Public/js/OrbitControls.js"></script>
    <script src="/Public/js/jquery-3.7.1.min.js"></script>
    <script src="/Public/js/geotiff.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #1a1a2e;
            font-family: Arial, sans-serif;
        }
    </style>
</head>

<body>

    <script type="module">
        const scene = new THREE.Scene();
        scene.background = new THREE.Color(0x1a1a2e);
        const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 10000);
        camera.position.set(4, 3.5, 3);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
        const ambientLight = new THREE.AmbientLight(0x404040);
        scene.add(ambientLight);
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
        directionalLight.position.set(1, 1, 1);
        scene.add(directionalLight);
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;

        //-------------------------------------

        const file = '/Public/imgs/dem/terrain2.tif';
        const res = await fetch(file)
        const arrayBuffer = await res.arrayBuffer();
        const tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer)
        const image = await tiff.getImage()
        const rasters = await image.readRasters()
        const width = image.getWidth()
        const height = image.getHeight()
        const elevationData = new Float32Array(rasters[0])
        const geometry = new THREE.PlaneGeometry(5, 5 * height / width, width - 1, height - 1)
        let maxElevation = -Infinity
        for (let i = 0; i < elevationData.length; i++) {
            if (elevationData[i] > maxElevation) {
                maxElevation = elevationData[i]
            }
        }
        let minElevation = Infinity
        for (let i = 0; i < elevationData.length; i++) {
            const num = elevationData[i]
            if (num !== 0 && num < minElevation) {
                minElevation = num
            }
        }
        const positions = geometry.attributes.position.array
        for (let i = 0; i < elevationData.length; i++) {
            if (maxElevation == minElevation) {
                positions[i * 3 + 2] = 0
            }
            else {
                positions[i * 3 + 2] = (elevationData[i] - minElevation) / (maxElevation - minElevation)
            }
        }
        geometry.attributes.position.needsUpdate = true
        geometry.computeVertexNormals()

        const base_mesh_material = new THREE.MeshBasicMaterial({
            color: '#ff0000',
            wireframe: true,
            transparent: true,
            opacity: 0.2
        })
        const base_mesh = new THREE.Mesh(geometry, base_mesh_material)
        base_mesh.rotateX(-Math.PI / 2);
        scene.add(base_mesh)

        const click_geometry = base_mesh.geometry
        const click_material = new THREE.ShaderMaterial({
            vertexShader: click_triangle_vs1,
            fragmentShader: click_triangle_fs1,
            side: THREE.DoubleSide,
            uniforms: {
                triangleA: { value: new THREE.Vector3() },
                triangleB: { value: new THREE.Vector3() },
                triangleC: { value: new THREE.Vector3() },
                bestNeighborA: { value: new THREE.Vector3() },
                bestNeighborB: { value: new THREE.Vector3() },
                bestNeighborC: { value: new THREE.Vector3() },
                showTriangle: { value: false },
                showBestNeighbor: { value: false }
            },
            transparent: true,
        })

        const click_mesh = new THREE.Mesh(geometry, click_material)
        click_mesh.rotateX(- Math.PI / 2)
        scene.add(click_mesh)

        const raycaster = new THREE.Raycaster()
        const mouse = new THREE.Vector2()

        function onMouseClick(event) {
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
            raycaster.setFromCamera(mouse, camera)
            const intersects = raycaster.intersectObject(base_mesh)

            if (intersects.length > 0) {
                const intersect = intersects[0]
                const face = intersect.face

                if (face) {
                    // 重置显示状态
                    click_material.uniforms.showTriangle.value = false
                    click_material.uniforms.showBestNeighbor.value = false

                    // 获取点击的三角形
                    const clickedTriangle = getTriangle(face.a, face.b, face.c)
                    click_material.uniforms.triangleA.value.copy(clickedTriangle.a)
                    click_material.uniforms.triangleB.value.copy(clickedTriangle.b)
                    click_material.uniforms.triangleC.value.copy(clickedTriangle.c)
                    click_material.uniforms.showTriangle.value = true

                    // 计算点击三角形的平均Y值
                    const clickedAvgY = (clickedTriangle.a.y + clickedTriangle.b.y + clickedTriangle.c.y) / 3

                    // 只有当clickedAvgY不为0时才查找相邻三角形
                    if (Math.abs(clickedAvgY) > 0.0001) { // 使用一个很小的阈值来避免浮点数精度问题
                        // 查找所有相邻三角形
                        const neighbors = findAdjacentTriangles(face.a, face.b, face.c, geometry, width, height)

                        // 找出Y值差最大的相邻三角形
                        let maxDiff = 0
                        let bestNeighbor = null

                        for (const neighbor of neighbors) {
                            const neighborAvgY = (neighbor.a.y + neighbor.b.y + neighbor.c.y) / 3
                            const diff = clickedAvgY - neighborAvgY // 点击三角形Y值减去相邻三角形Y值

                            if (diff > maxDiff) {
                                maxDiff = diff
                                bestNeighbor = neighbor
                            }
                        }

                        // 如果找到符合条件的相邻三角形，显示它
                        if (bestNeighbor && maxDiff > 0) {
                            click_material.uniforms.bestNeighborA.value.copy(bestNeighbor.a)
                            click_material.uniforms.bestNeighborB.value.copy(bestNeighbor.b)
                            click_material.uniforms.bestNeighborC.value.copy(bestNeighbor.c)
                            click_material.uniforms.showBestNeighbor.value = true

                            console.log(`找到差值最大的相邻三角形，差值: ${maxDiff.toFixed(4)}`)
                        } else {
                            console.log('没有找到Y值更小的相邻三角形')
                        }
                    } else {
                        console.log('clickedAvgY为0，不查找相邻三角形')
                    }
                }
            }

            click_material.uniformsNeedUpdate = true
        }

        // 辅助函数：获取三角形顶点（世界坐标）
        function getTriangle(aIndex, bIndex, cIndex) {
            const positions = geometry.attributes.position.array

            const getVertexPosition = (index) => {
                const x = positions[index * 3]
                const y = positions[index * 3 + 1]
                const z = positions[index * 3 + 2]
                const vertex = new THREE.Vector3(x, y, z)
                vertex.applyMatrix4(base_mesh.matrixWorld)
                return vertex
            }

            return {
                a: getVertexPosition(aIndex),
                b: getVertexPosition(bIndex),
                c: getVertexPosition(cIndex)
            }
        }

        // 辅助函数：查找相邻三角形
        function findAdjacentTriangles(aIndex, bIndex, cIndex, geometry, width, height) {
            const neighbors = []

            // 由于是PlaneGeometry创建的规则网格，我们可以直接根据顶点索引查找相邻三角形
            // 每个四边形由2个三角形组成，相邻三角形共享一条边

            const indexArray = geometry.index ? geometry.index.array : null

            if (indexArray) {
                // 获取当前三角形的三条边
                const edges = [
                    [aIndex, bIndex],
                    [bIndex, cIndex],
                    [cIndex, aIndex]
                ]

                // 查找所有共享边的三角形
                for (let i = 0; i < indexArray.length; i += 3) {
                    const triA = indexArray[i]
                    const triB = indexArray[i + 1]
                    const triC = indexArray[i + 2]

                    // 跳过当前点击的三角形
                    if ((triA === aIndex && triB === bIndex && triC === cIndex) ||
                        (triA === aIndex && triB === cIndex && triC === bIndex) ||
                        (triA === bIndex && triB === aIndex && triC === cIndex) ||
                        (triA === bIndex && triB === cIndex && triC === aIndex) ||
                        (triA === cIndex && triB === aIndex && triC === bIndex) ||
                        (triA === cIndex && triB === bIndex && triC === aIndex)) {
                        continue
                    }

                    // 检查是否共享一条边（两个顶点相同）
                    for (const [edgeV1, edgeV2] of edges) {
                        const hasEdgeV1 = triA === edgeV1 || triB === edgeV1 || triC === edgeV1
                        const hasEdgeV2 = triA === edgeV2 || triB === edgeV2 || triC === edgeV2

                        if (hasEdgeV1 && hasEdgeV2) {
                            // 找到共享边的相邻三角形
                            neighbors.push(getTriangle(triA, triB, triC))
                            break // 已经找到是相邻三角形，跳出内层循环
                        }
                    }
                }
            }

            return neighbors
        }

        window.addEventListener('click', onMouseClick, false)

        //-------------------------------------

        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }

        // 启动动画
        animate();

        // 窗口大小调整
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>

</html>